/*******************************************************************************
 * Copyright (c) baifeiq.
 * 
 * @date            @author         @brief
 * 2021-02-05       baifeiq         First version: 
 */

#include "os_define.h"
#include "os_list.h"
#include "os_task.h"
#include "os_event.h"

/**
 * @brief Initialization event data structure.
 * 
 * @param event Initialized event.
 * @param type event type.
 */
void ost_event_init(os_event_t *event, os_event_type_t type)
{
    event->type =   type;
    os_list_init(&event->list);
}

/**
 * @brief Set the interface waiting for the event.
 * 
 * @param event Set the event that the task needs to wait.
 * @param task Tasks that need to wait for events.
 * @param msg Event information storage location.
 * @param state Task state.
 * @param timeout Timeout waiting for the event.
 */
void ost_event_wait(os_event_t   *event, 
                    os_task_t    *task, 
                    void         *msg, 
                    os_uint32_t  state,
                    os_uint32_t  timeout)
{
    os_uint32_t os_interrupt = os_critical_enter();

    task->state     =   state;
    task->event     =   event;
    task->event_msg =   msg;
    task->event_result  =   0;

    os_remove_ready(task);
    os_list_insert_after(&event->list, &task->list_event);

    if (timeout)
    {
        ost_delay(timeout);
    }

    os_sched();

    os_critical_quit(os_interrupt);
}

/**
 * @brief Wake the corresponding task by the event.
 * 
 * @param event Wake-up event.
 * @param msg Event information storage location.
 * @param result Results of the event wake up.
 */
os_task_t *ost_event_wake(os_event_t *event, void *msg, os_uint32_t result)
{
    os_list_t *node;
    os_task_t *task = (os_task_t *)0;

    os_uint32_t os_interrupt = os_critical_enter();

    if ((node = os_list_out_last(&event->list)) != (os_list_t *)0)
    {
        task = (os_task_t *)os_list_entry(node, os_task_t, list_event);

        task->event         =   (os_event_t *)0;
        task->event_msg     =   msg;
        task->event_result  =   result;
        task->state         =   OS_READY;

        if (task->timer != 0)
        {
            ost_delay_wake(task);
        }

        os_insert_ready(task);
    }

    os_critical_quit(os_interrupt);

    return task;
}

/**
 * @brief Directly remove events.
 * 
 * @param task Tasks that need to delete events.
 * @param msg Storage location of an event message.
 * @param result Event waiting result.
 */
void ost_event_remove(os_task_t *task, void *msg, os_uint32_t result)
{
    os_uint32_t os_interrupt = os_critical_enter();

    os_list_remove(&task->list_event);

    task->event         =   (os_event_t *)0;
    task->event_msg     =   msg;
    task->event_result  =   result;
    task->state         =   OS_WAITE_DELAY;

    os_critical_quit(os_interrupt);
}
